package com.vision.sales.salesman.cl.ui;

import static com.vision.sales.cl.SalesServices.salesmanSvc;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.core.client.GWT;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.SimpleBeanEditorDriver;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.Style.SelectionMode;
import com.sencha.gxt.data.client.loader.RpcProxy;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.widget.core.client.TabItemConfig;
import com.sencha.gxt.widget.core.client.Window;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.form.NumberField;
import com.sencha.gxt.widget.core.client.form.SimpleComboBox;
import com.sencha.gxt.widget.core.client.form.TextArea;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.form.validator.MaxLengthValidator;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.grid.editing.GridInlineEditing;
import com.sencha.gxt.widget.core.client.grid.filters.GridFilters;
import com.sencha.gxt.widget.core.client.grid.filters.ListFilter;
import com.sencha.gxt.widget.core.client.grid.filters.StringFilter;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.SelectionChangedHandler;
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar;
import com.vision.core.cl.BaseModule;
import com.vision.core.cl.Callback;
import com.vision.core.cl.event.CreatedEvent;
import com.vision.core.cl.event.CreatedEvent.CreationHandler;
import com.vision.core.cl.ui.ConfirmDialogHandler;
import com.vision.core.cl.ui.TabItemPanel;
import com.vision.core.cl.ui.UI;
import com.vision.core.cl.ui.component.BasicGridView;
import com.vision.core.cl.ui.component.EnumComboBox;
import com.vision.sales.cl.SalesServices;
import com.vision.sales.cl.SalesTasks;
import com.vision.sales.cl.data.SalesInvoiceProperties;
import com.vision.sales.cl.data.SalesmanProductProperties;
import com.vision.sales.cl.data.SalesmanProperties;
import com.vision.sales.cl.event.SalesmanCreatedEvent;
import com.vision.sales.cl.resource.SalesResources;
import com.vision.sales.cl.ui.UIConstants;
import com.vision.sales.cl.ui.component.SalesmanComboBox;
import com.vision.sales.cl.ui.component.TransactionLoadConfig;
import com.vision.sales.cl.ui.component.TransactionNoCell;
import com.vision.sales.cm.db.data.SalesInvoice;
import com.vision.sales.cm.db.data.Salesman;
import com.vision.sales.cm.db.data.Salesman.FieldSpecs;
import com.vision.sales.cm.db.data.Salesman.SalesmanStatus;
import com.vision.sales.cm.db.data.SalesmanProduct;

/**
 * 
 * @author Mark
 *
 */
public class SalesmenPanel extends TabItemPanel implements Editor<Salesman> {

	interface Binder extends UiBinder<Widget, SalesmenPanel> { }

	private static final Binder binder = GWT.create(Binder.class);

	@UiField VerticalLayoutContainer mainPanel;

	@Ignore @UiField TextButton refreshBtn;
	@Ignore @UiField TextButton addBtn;
	@Ignore @UiField TextButton deleteBtn;
	
	//@UiField Grid<Salesman> grid;
	@Ignore @UiField SalesmanComboBox salesmanComboBox;
	
	@UiField TextField code;
	@UiField TextField name;
	@UiField TextField phone;
	@UiField TextArea address;
	@UiField TextArea comment;
	@UiField SimpleComboBox<SalesmanStatus> status;
	
	@Ignore @UiField TextButton saveBtn;
	@Ignore @UiField TextButton cancelBtn;
	
	// Interface used to bind the form fields to the Salesman fields
	interface SalesmanDriver extends SimpleBeanEditorDriver<Salesman, SalesmenPanel> { }

	// Instance of the ProductDriver interface
	private SalesmanDriver driver = GWT.create(SalesmanDriver.class);;
	
	@Ignore @UiField TextButton saveCommissionRatesBtn;
	@UiField(provided = true) Grid<SalesmanProduct> commissionsGrid;
	
	@UiField(provided = true) Grid<SalesInvoice> salesGrid;
	@UiField PagingToolBar salesGridPagingToolbar;
	PagingLoader<PagingLoadConfig, PagingLoadResult<SalesInvoice>> salesGridLoader;
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public SalesmenPanel() {
		// Global Handlers -- should be registered only once
		BaseModule.addHandler(SalesmanCreatedEvent.TYPE, new CreationHandler<Salesman>() {
			@Override
			public void onCreated(CreatedEvent<Salesman> e) {
				//grid.getStore().add(e.getObject());
				salesmanComboBox.getStore().add(e.getObject());
			}
		});
		
		commissionsGrid = initializeSalesmanProductsGrid();
		salesGrid = initializeSalesGrid();
		
		setWidget(binder.createAndBindUi(this));
		
		// Initialize the driver, used to run the form editor
		driver.initialize(this);
		
		code.addValidator(new MaxLengthValidator(FieldSpecs.CODE_MAX));
		name.addValidator(new MaxLengthValidator(FieldSpecs.NAME_MAX));
		address.addValidator(new MaxLengthValidator(FieldSpecs.ADDRESS_MAX));
		phone.addValidator(new MaxLengthValidator(FieldSpecs.PHONE_MAX));
		comment.addValidator(new MaxLengthValidator(FieldSpecs.COMMENT_MAX));
		
		{
			// Initialize Loader for sales grid
			RpcProxy<PagingLoadConfig, PagingLoadResult<SalesInvoice>> proxy = new RpcProxy<PagingLoadConfig, PagingLoadResult<SalesInvoice>>() {
			    @Override
			    public void load(PagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<SalesInvoice>> callback) {
			    	Salesman salesman = salesmanComboBox.getCurrentValue();
			    	if (salesman == null) return;
			    	TransactionLoadConfig tConfig = new TransactionLoadConfig(loadConfig);
			    	tConfig.setSalesmen(Arrays.asList(new Salesman[]{ salesman }));
			    	SalesServices.salesInvoiceSvc().getList(tConfig, callback);
			    }
		    };
		    
		    salesGridLoader = new PagingLoader<PagingLoadConfig, PagingLoadResult<SalesInvoice>>(proxy);
		    salesGridLoader.addLoadHandler(new LoadResultListStoreBinding<PagingLoadConfig, SalesInvoice, PagingLoadResult<SalesInvoice>>(salesGrid.getStore()));
	        
	        salesGrid.setLoader(salesGridLoader);
	        salesGrid.setLoadMask(true);
			
			salesGridPagingToolbar.bind(salesGridLoader);
		}
	}
	
	@Ignore
	@UiFactory
	SimpleComboBox<SalesmanStatus> createSalesmanStatusCombo() {
		return new EnumComboBox<SalesmanStatus>(SalesmanStatus.values());
	}
	
	@UiFactory
	Grid<Salesman> initializeGrid() {
		SalesmanProperties props = GWT.create(SalesmanProperties.class);
		
		List<ColumnConfig<Salesman, ?>> columns = new ArrayList<ColumnConfig<Salesman, ?>>();
		columns.add(UI.column(props.code(), UIConstants.CODE_COL_WIDTH, "Code"));
		ColumnConfig<Salesman, String> name = UI.column(props.name(), UIConstants.NAME_COL_WIDTH, "Name");
		columns.add(name);
		columns.add(UI.column(props.address(), UIConstants.COMMENT_COL_WIDTH, "Address"));
		columns.add(UI.column(props.phone(),  UIConstants.PHONE_COL_WIDTH, "Phone"));
		columns.add(UI.column(props.status(), UIConstants.STATUS_COL_WIDTH, "Status"));
		
		ColumnModel<Salesman> cm = new ColumnModel<Salesman>(columns);

		final Grid<Salesman> grid = new Grid<Salesman>(new ListStore<Salesman>(props.key()), cm);
		grid.setAllowTextSelection(false);
		grid.setHeight(300);
		grid.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
		
		GridView<Salesman> view = new BasicGridView<Salesman>();
		view.setEmptyText("There are no salesmen to display.");
		view.setAutoExpandColumn(name);
		grid.setView(view);
		
		addGridListeners(grid);
		
		addGridFilters(grid, props);
			
		return grid;
	}

	private void addGridListeners(Grid<Salesman> grd) {
		// Add a listener to the grid
		grd.getSelectionModel().addSelectionChangedHandler(new SelectionChangedHandler<Salesman>() {
			@Override
			public void onSelectionChanged(SelectionChangedEvent<Salesman> event) {
				final Salesman salesman = salesmanComboBox.getCurrentValue(); 
				// grid.getSelectionModel().getSelectedItem();
				if (salesman == null)
					return;
				
				loadChildren(salesman);
				
				if (driver.isDirty()) { // If there are unsaved changes in the form
					UI.confirm("Unsaved Changes", "There are unsaved changes. Would you like to continue loading?",
						new ConfirmDialogHandler() {
							@Override
							public void onYes(HideEvent event) {
								edit(salesman);
							}
						}
					);
				} else {
					edit(salesman);
				}
			}
		});
	}

	private void addGridFilters(Grid<Salesman> grd, SalesmanProperties props) {
		ListStore<SalesmanStatus> statusStore = new ListStore<SalesmanStatus>(new ModelKeyProvider<SalesmanStatus>() {
			@Override
			public String getKey(SalesmanStatus status) {
				return status.toString();
			}
		});
		statusStore.addAll(Arrays.asList(SalesmanStatus.values()));
		
		StringFilter<Salesman> codeFilter = new StringFilter<Salesman>(props.code());
	    StringFilter<Salesman> nameFilter = new StringFilter<Salesman>(props.name());
	    StringFilter<Salesman> phoneFilter = new StringFilter<Salesman>(props.phone());
	    StringFilter<Salesman> addressFilter = new StringFilter<Salesman>(props.address());
	    StringFilter<Salesman> commentFilter = new StringFilter<Salesman>(props.comment());
	    ListFilter<Salesman, SalesmanStatus> statusFilter = new ListFilter<Salesman, SalesmanStatus>(props.status(), statusStore);
	    
	    GridFilters<Salesman> filters = new GridFilters<Salesman>();
	    filters.initPlugin(grd);
	    filters.setLocal(true);
	    filters.addFilter(codeFilter);
	    filters.addFilter(nameFilter);
	    filters.addFilter(phoneFilter);
	    filters.addFilter(addressFilter);
	    filters.addFilter(commentFilter);
	    filters.addFilter(statusFilter);
	}
	
	Grid<SalesmanProduct> initializeSalesmanProductsGrid() {
		SalesmanProductProperties props = SalesmanProductProperties.INSTANCE;
		
		List<ColumnConfig<SalesmanProduct, ?>> columns = new ArrayList<ColumnConfig<SalesmanProduct, ?>>();
		
		ColumnConfig<SalesmanProduct, Double> commissionRateCol;
		
		columns.add(UI.column(props.productCode(), 200, "Product"));
		columns.add(commissionRateCol = UI.column(props.commissionRate(), 160, "Commission Rate (%)", UI.decimalCell(), HasHorizontalAlignment.ALIGN_RIGHT));
		
		ListStore<SalesmanProduct> store = new ListStore<SalesmanProduct>(props.key());
		
		GridView<SalesmanProduct> view = new BasicGridView<SalesmanProduct>();
		view.setEmptyText("There are no salesman-product data to display.");
		
		Grid<SalesmanProduct> grid = new Grid<SalesmanProduct>(store, new ColumnModel<SalesmanProduct>(columns), view);
		
		// Editing
		GridInlineEditing<SalesmanProduct> editing = new GridInlineEditing<SalesmanProduct>(grid);
		
		NumberField<Double> field = new NumberField<Double>(UI.doubleEditor());
		field.setClearValueOnParseError(true);
		
		editing.addEditor(commissionRateCol, field);
		
		return grid;
	}
	
	Grid<SalesInvoice> initializeSalesGrid() {
		SalesInvoiceProperties props = SalesInvoiceProperties.INSTANCE;
		ListStore<SalesInvoice> store = new ListStore<SalesInvoice>(props.key());
		
		List<ColumnConfig<SalesInvoice, ?>> columns = new ArrayList<ColumnConfig<SalesInvoice, ?>>();

		columns.add(UI.column(props.date(), 40, "Date", UI.dateCell()));
//		columns.add(UI.column(props.companyCode(), 40, "Company"));
		columns.add(UI.column(props.groupAndNo(), 45, "Invoice No.", new TransactionNoCell<SalesInvoice>(store)));
		columns.add(UI.column(props.customerCode(), 70, "Customer"));
		columns.add(UI.column(props.productCode(), 50, "Product"));
		columns.add(UI.column(props.poNo(), 60, "PO No."));
		columns.add(UI.column(props.grossSales(), 55, "Gross Sales", UI.decimalCell(), HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.balance(), 55, "Balance", UI.decimalCell(), HasHorizontalAlignment.ALIGN_RIGHT));
//		columns.add(UI.column(props.status(), 40, "Status", null, HasHorizontalAlignment.ALIGN_CENTER));
		
		ColumnModel<SalesInvoice> cm = new ColumnModel<SalesInvoice>(columns);

		Grid<SalesInvoice> grid = new Grid<SalesInvoice>(store, cm);
		grid.setAllowTextSelection(false);
		grid.getSelectionModel().setSelectionMode(SelectionMode.MULTI);
		grid.setHeight(300);
		
		GridView<SalesInvoice> view = new BasicGridView<SalesInvoice>();
		view.setAutoExpandColumn(columns.get(2));
		view.setEmptyText("There are no sales invoices to display.");
		grid.setView(view);
		
		return grid;
	}
	
	// Logic %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	private void edit(final Salesman salesman) {
		driver.edit(salesman);

		refreshUIControls(salesman != null);
		
		if (salesman == null)
			return;
		
		UI.mask(commissionsGrid, "Loading salesman's products...");
		salesmanSvc().getSalesmanProducts(salesman.getId(), new Callback<Set<SalesmanProduct>>(commissionsGrid) {
			@Override
			protected void handleSuccess(Set<SalesmanProduct> result) {
				commissionsGrid.getStore().clear();
				commissionsGrid.getStore().addAll(result);
			}
		});
		
		salesGridLoader.load();
	}
	
	// Listeners %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

	private void refreshUIControls(boolean enabled) {
		deleteBtn.setEnabled(enabled);
		
		saveBtn.setEnabled(enabled);
		cancelBtn.setEnabled(enabled);

		name.setEnabled(enabled);
		phone.setEnabled(enabled);
		address.setEnabled(enabled);
		comment.setEnabled(enabled);
		status.setEnabled(enabled);
	}

	@UiHandler("salesmanComboBox")
	void salesmanComboBoxOnSelection(SelectionEvent<Salesman> e) {
		Salesman selectedSalesman = e.getSelectedItem();
		edit(selectedSalesman);
	}
	
	// Called when the 'Refresh' button is clicked
	@UiHandler("refreshBtn")
	void refreshClicked(SelectEvent e) {
		loadData();
	}
	
	// Called when the 'Add' button is clicked
	@UiHandler("addBtn")
	void addClicked(SelectEvent e) {
		Window w = (Window) new CreateSalesmanWindow().asWidget();
		w.show();
	}
	
	// Called when the 'Delete' button is clicked
	@UiHandler("deleteBtn")
	void deleteClicked(SelectEvent e) {
		final Salesman selected = salesmanComboBox.getCurrentValue();
				//grid.getSelectionModel().getSelectedItem();
		if (selected == null)
			return;
		
		UI.confirm("Confirm Delete", "Are you sure you want to delete '" + selected.getCode() + "'?",
			new ConfirmDialogHandler() {
				@Override
				public void onYes(HideEvent event) {
					UI.mask(mainPanel, "Deleting " + selected.getCode() + "...");
					salesmanSvc().delete(selected, new Callback<Salesman>(mainPanel) {
						@Override
						protected void handleSuccess(Salesman result) {
							//grid.getStore().remove(result);
							salesmanComboBox.getStore().remove(result);
							
							// Clear the form editor if necessary
							Salesman inEditor = driver.flush();
							if (inEditor != null && inEditor.getCode().equals(result.getCode()))
								edit(null);
						}
					});
				}
			}
		);
	}
	
	// Called when the 'Save' button is clicked
	@UiHandler("saveBtn")
	void saveClicked(SelectEvent e) {
		Salesman salesman = driver.flush();
		if (driver.hasErrors())
			return;
		
		UI.mask(mainPanel, "Updating salesman...");
		salesmanSvc().update(salesman, new Callback<Salesman>(mainPanel) {
			@Override
			protected void handleSuccess(Salesman result) {
				salesmanComboBox.getStore().update(result);
				edit(result);
			}
		});
	}
	
	// Called when the 'Cancel' button is clicked
	@UiHandler("cancelBtn")
	void cancelClicked(SelectEvent e) {
		edit(null);
	}
	
	// Called when the 'Save' (for commission rates) button is clicked
	@UiHandler("saveCommissionRatesBtn")
	void saveCommissionRatesClicked(SelectEvent e) {
		commissionsGrid.getStore().commitChanges();
		
		// The List returned by getStore().getAll() is not serializable, so we wrap it with an ArrayList
		List<SalesmanProduct> list = new ArrayList<SalesmanProduct>(commissionsGrid.getStore().getAll());
		UI.mask(commissionsGrid, "Saving commission rates...");
		salesmanSvc().saveCommissionRates(list, new Callback<Void>(commissionsGrid) {
			@Override
			protected void handleSuccess(Void result) {
				super.handleSuccess(result);
			}
		});
	}
	
	// ITabItemPanel Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@Override
	public String getPanelId() {
		return SalesTasks.SALESMEN.getId();
	}
	
	@Override
	public TabItemConfig getConfig() {
		if (config == null) {
			config = new TabItemConfig("Salesmen", true);
			config.setIcon(SalesResources.INSTANCE.salesmen());
		}
		return config;
	}
	
	@Override
	public void loadData() {
		Log.debug("Loading salesmen...");

		UI.mask(mainPanel, "Loading salesmen...");
		salesmanSvc().getAll(new Callback<List<Salesman>>(mainPanel) {
			@Override
			protected void handleSuccess(List<Salesman> result) {
//				grid.getStore().clear();
//				grid.getStore().addAll(result);
				salesmanComboBox.getStore().clear();
				salesmanComboBox.getStore().addAll(result);
				
				edit(null); // Clear the form
			}
		});
	}

	private void loadChildren(Salesman salesman) {
//		Log.debug("Loading salesmen...");
//
//		UI.mask(mainPanel, "Loading salesmen...");
//		salesmanSvc().getSalesmanProducts(salesman.getId(), new Callback<Set<SalesmanProduct>>(mainPanel) {
//			@Override
//			protected void handleSuccess(Set<SalesmanProduct> result) {
////				productsGrid.getStore().clear();
////				productsGrid.getStore().addAll(result);
////				
////				edit(null); // Clear the form
//			}
//		});
	}

}
